/*
 * Copyright (c) Tony Bybell 2008-2014.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 */

#include <config.h>
#include "globals.h"
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include "gtk23compat.h"
#include "analyzer.h"
#include "tree.h"
#include "symbol.h"
#include "vcd.h"
#include "lx2.h"
#include "busy.h"
#include "debug.h"
#include "hierpack.h"
#include "menu.h"
#include "tcl_helper.h"
#include "tcl_support_commands.h"
#include "signal_list.h"
#include "wave_view.h"

#if !defined __MINGW32__
#include <sys/types.h>
#include <unistd.h>
#endif

#if defined(HAVE_LIBTCL)
#include <tcl.h>
#endif

/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
/* XXX functions for embedding TCL interpreter XXX */
/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */

#if defined(HAVE_LIBTCL)

static int gtkwavetcl_badNumArgs(ClientData clientData,
                                 Tcl_Interp *interp,
                                 int objc,
                                 Tcl_Obj *CONST objv[],
                                 int expected)
{
    (void)clientData;

    Tcl_Obj *aobj;
    char reportString[1024];

    sprintf(reportString,
            "* wrong number of arguments for '%s': %d expected, %d encountered",
            Tcl_GetString(objv[0]),
            expected,
            objc - 1);

    aobj = Tcl_NewStringObj(reportString, -1);
    Tcl_SetObjResult(interp, aobj);
    return (TCL_ERROR);
}

static int gtkwavetcl_nop(ClientData clientData,
                          Tcl_Interp *interp,
                          int objc,
                          Tcl_Obj *CONST objv[])
{
    (void)clientData;
    (void)interp;
    (void)objc;
    (void)objv;

    /* nothing, this is simply to call gtk's main loop */
    gtkwave_main_iteration();
    return (TCL_OK);
}

static int gtkwavetcl_printInteger(ClientData clientData,
                                   Tcl_Interp *interp,
                                   int objc,
                                   Tcl_Obj *CONST objv[],
                                   int intVal)
{
    (void)clientData;
    (void)objc;
    (void)objv;

    Tcl_Obj *aobj;
    char reportString[33];

    sprintf(reportString, "%d", intVal);

    aobj = Tcl_NewStringObj(reportString, -1);
    Tcl_SetObjResult(interp, aobj);

    return (TCL_OK);
}

static int gtkwavetcl_printTimeType(ClientData clientData,
                                    Tcl_Interp *interp,
                                    int objc,
                                    Tcl_Obj *CONST objv[],
                                    TimeType ttVal)
{
    (void)clientData;
    (void)objc;
    (void)objv;

    Tcl_Obj *aobj;
    char reportString[65];

    sprintf(reportString, TTFormat, ttVal);

    aobj = Tcl_NewStringObj(reportString, -1);
    Tcl_SetObjResult(interp, aobj);

    return (TCL_OK);
}

static int gtkwavetcl_printTraceFlagsType(ClientData clientData,
                                          Tcl_Interp *interp,
                                          int objc,
                                          Tcl_Obj *CONST objv[],
                                          TraceFlagsType ttVal)
{
    (void)clientData;
    (void)objc;
    (void)objv;

    Tcl_Obj *aobj;
    char reportString[65];

    sprintf(reportString, "%" TRACEFLAGSPRIuFMT, ttVal);

    aobj = Tcl_NewStringObj(reportString, -1);
    Tcl_SetObjResult(interp, aobj);

    return (TCL_OK);
}

static int gtkwavetcl_printDouble(ClientData clientData,
                                  Tcl_Interp *interp,
                                  int objc,
                                  Tcl_Obj *CONST objv[],
                                  double dVal)
{
    (void)clientData;
    (void)objc;
    (void)objv;

    Tcl_Obj *aobj;
    char reportString[65];

    sprintf(reportString, "%e", dVal);

    aobj = Tcl_NewStringObj(reportString, -1);
    Tcl_SetObjResult(interp, aobj);

    return (TCL_OK);
}

static int gtkwavetcl_printString(ClientData clientData,
                                  Tcl_Interp *interp,
                                  int objc,
                                  Tcl_Obj *CONST objv[],
                                  const char *reportString)
{
    (void)clientData;
    (void)objc;
    (void)objv;

    Tcl_Obj *aobj;

    aobj = Tcl_NewStringObj(reportString, -1);
    Tcl_SetObjResult(interp, aobj);

    return (TCL_OK);
}

static char *extractFullTraceName(Trptr t)
{
    char *name = NULL;

    if (HasWave(t)) {
        if (HasAlias(t)) {
            name = strdup_2(t->name_full);
        } else if (t->vector) {
            name = strdup_2(t->n.vec->bvname);
        } else {
            int flagged = HIER_DEPACK_ALLOC;

            name = hier_decompress_flagged(t->n.nd->nname, &flagged);
            if (!flagged) {
                name = strdup_2(name);
            }
        }
    }
    return (name);
}

/* tcl interface functions */

char *get_Tcl_string(Tcl_Obj *obj)
{
    char *s = Tcl_GetString(obj);
    if (*s == '{') { /* braced string */
        char *p = strrchr(s, '}');
        if (p) {
            if (GLOBALS->previous_braced_tcl_string) {
                free_2(GLOBALS->previous_braced_tcl_string);
            }

            GLOBALS->previous_braced_tcl_string = strdup_2(s);
            GLOBALS->previous_braced_tcl_string[p - s] = 0;
            s = GLOBALS->previous_braced_tcl_string + 1;
        }
    }
    return s;
}

static int gtkwavetcl_getNumFacs(ClientData clientData,
                                 Tcl_Interp *interp,
                                 int objc,
                                 Tcl_Obj *CONST objv[])
{
    int value = GLOBALS->numfacs;
    return (gtkwavetcl_printInteger(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getLongestName(ClientData clientData,
                                     Tcl_Interp *interp,
                                     int objc,
                                     Tcl_Obj *CONST objv[])
{
    int value = GLOBALS->longestname;
    return (gtkwavetcl_printInteger(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getFacName(ClientData clientData,
                                 Tcl_Interp *interp,
                                 int objc,
                                 Tcl_Obj *CONST objv[])
{
    Tcl_Obj *aobj;

    if (objc == 2) {
        char *s = get_Tcl_string(objv[1]);
        int which = atoi(s);

        if ((which >= 0) && (which < GLOBALS->numfacs)) {
            int was_packed = HIER_DEPACK_ALLOC;
            char *hfacname = NULL;

            hfacname = hier_decompress_flagged(GLOBALS->facs[which]->name, &was_packed);

            aobj = Tcl_NewStringObj(hfacname, -1);
            Tcl_SetObjResult(interp, aobj);
            if (was_packed)
                free_2(hfacname);
        }
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    return (TCL_OK);
}

static int gtkwavetcl_getFacDir(ClientData clientData,
                                Tcl_Interp *interp,
                                int objc,
                                Tcl_Obj *CONST objv[])
{
    Tcl_Obj *aobj;

    if (objc == 2) {
        char *s = get_Tcl_string(objv[1]);
        int which = atoi(s);

        if ((which >= 0) && (which < GLOBALS->numfacs)) {
            WAVE_NODEVARDIR_STR
            int vardir =
                GLOBALS->facs[which]
                    ->n->vardir; /* two bit already chops down to 0..3, but this doesn't hurt */
            if ((vardir < 0) || (vardir > ND_DIR_MAX)) {
                vardir = 0;
            }

            aobj = Tcl_NewStringObj(vardir_strings[vardir], -1);
            Tcl_SetObjResult(interp, aobj);
        }
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    return (TCL_OK);
}

static int gtkwavetcl_getFacVtype(ClientData clientData,
                                  Tcl_Interp *interp,
                                  int objc,
                                  Tcl_Obj *CONST objv[])
{
    Tcl_Obj *aobj;

    if (objc == 2) {
        char *s = get_Tcl_string(objv[1]);
        int which = atoi(s);

        if ((which >= 0) && (which < GLOBALS->numfacs)) {
            WAVE_NODEVARTYPE_STR
            WAVE_NODEVARDATATYPE_STR
            unsigned int varxt;
            char *varxt_pnt;
            int vartype;
            int vardt;

            varxt = GLOBALS->facs[which]->n->varxt;
            varxt_pnt = varxt ? varxt_fix(GLOBALS->subvar_pnt[varxt]) : NULL;

            vartype = GLOBALS->facs[which]->n->vartype;
            if ((vartype < 0) || (vartype > ND_VARTYPE_MAX)) {
                vartype = 0;
            }

            vardt = GLOBALS->facs[which]->n->vardt;
            if ((vardt < 0) || (vardt > ND_VDT_MAX)) {
                vardt = 0;
            }

            aobj = Tcl_NewStringObj((((GLOBALS->supplemental_datatypes_encountered) &&
                                      (!GLOBALS->supplemental_vartypes_encountered))
                                         ? (varxt ? varxt_pnt : vardatatype_strings[vardt])
                                         : vartype_strings[vartype]),
                                    -1);
            Tcl_SetObjResult(interp, aobj);
        }
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    return (TCL_OK);
}

static int gtkwavetcl_getFacDtype(ClientData clientData,
                                  Tcl_Interp *interp,
                                  int objc,
                                  Tcl_Obj *CONST objv[])
{
    Tcl_Obj *aobj;

    if (objc == 2) {
        char *s = get_Tcl_string(objv[1]);
        int which = atoi(s);

        if ((which >= 0) && (which < GLOBALS->numfacs)) {
            WAVE_NODEVARDATATYPE_STR
            unsigned int varxt;
            char *varxt_pnt;
            int vardt;

            varxt = GLOBALS->facs[which]->n->varxt;
            varxt_pnt = varxt ? varxt_fix(GLOBALS->subvar_pnt[varxt]) : NULL;

            vardt = GLOBALS->facs[which]->n->vardt;
            if ((vardt < 0) || (vardt > ND_VDT_MAX)) {
                vardt = 0;
            }

            aobj = Tcl_NewStringObj(varxt ? varxt_pnt : vardatatype_strings[vardt], -1);
            Tcl_SetObjResult(interp, aobj);
        }
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    return (TCL_OK);
}

static int gtkwavetcl_getMinTime(ClientData clientData,
                                 Tcl_Interp *interp,
                                 int objc,
                                 Tcl_Obj *CONST objv[])
{
    TimeType value = GLOBALS->min_time;
    return (gtkwavetcl_printTimeType(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getMaxTime(ClientData clientData,
                                 Tcl_Interp *interp,
                                 int objc,
                                 Tcl_Obj *CONST objv[])
{
    TimeType value = GLOBALS->max_time;
    return (gtkwavetcl_printTimeType(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getTimeZero(ClientData clientData,
                                  Tcl_Interp *interp,
                                  int objc,
                                  Tcl_Obj *CONST objv[])
{
    TimeType value = GLOBALS->global_time_offset;
    return (gtkwavetcl_printTimeType(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getTimeDimension(ClientData clientData,
                                       Tcl_Interp *interp,
                                       int objc,
                                       Tcl_Obj *CONST objv[])
{
    (void)clientData;
    (void)objc;
    (void)objv;

    Tcl_Obj *aobj;
    char reportString[2];

    reportString[0] = GLOBALS->time_dimension;
    reportString[1] = 0;

    aobj = Tcl_NewStringObj(reportString, -1);
    Tcl_SetObjResult(interp, aobj);

    return (TCL_OK);
}

static int gtkwavetcl_getArgv(ClientData clientData,
                              Tcl_Interp *interp,
                              int objc,
                              Tcl_Obj *CONST objv[])
{
    (void)clientData;
    (void)objc;
    (void)objv;

    if (GLOBALS->argvlist) {
        Tcl_Obj *aobj = Tcl_NewStringObj(GLOBALS->argvlist, -1);
        Tcl_SetObjResult(interp, aobj);
    }

    return (TCL_OK);
}

static int gtkwavetcl_getBaselineMarker(ClientData clientData,
                                        Tcl_Interp *interp,
                                        int objc,
                                        Tcl_Obj *CONST objv[])
{
    TimeType value = GLOBALS->tims.baseline;
    return (gtkwavetcl_printTimeType(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getMarker(ClientData clientData,
                                Tcl_Interp *interp,
                                int objc,
                                Tcl_Obj *CONST objv[])
{
    TimeType value = GLOBALS->tims.marker;
    return (gtkwavetcl_printTimeType(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getWindowStartTime(ClientData clientData,
                                         Tcl_Interp *interp,
                                         int objc,
                                         Tcl_Obj *CONST objv[])
{
    TimeType value = GLOBALS->tims.start;
    return (gtkwavetcl_printTimeType(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getWindowEndTime(ClientData clientData,
                                       Tcl_Interp *interp,
                                       int objc,
                                       Tcl_Obj *CONST objv[])
{
    TimeType value = GLOBALS->tims.end;
    return (gtkwavetcl_printTimeType(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getDumpType(ClientData clientData,
                                  Tcl_Interp *interp,
                                  int objc,
                                  Tcl_Obj *CONST objv[])
{
    (void)clientData;
    (void)objc;
    (void)objv;

    Tcl_Obj *aobj;
    const char *reportString = "UNKNOWN";

    if (GLOBALS->is_vcd) {
        if (GLOBALS->partial_vcd) {
            reportString = "PVCD";
        } else {
            reportString = "VCD";
        }
    } else if (GLOBALS->is_lxt) {
        reportString = "LXT";
    } else if (GLOBALS->is_ghw) {
        reportString = "GHW";
    } else if (GLOBALS->is_lx2) {
        switch (GLOBALS->is_lx2) {
            case LXT2_IS_LXT2:
                reportString = "LXT2";
                break;
            case LXT2_IS_AET2:
                reportString = "AET2";
                break;
            case LXT2_IS_VZT:
                reportString = "VZT";
                break;
            case LXT2_IS_VLIST:
                reportString = "VCD";
                break;
            case LXT2_IS_FST:
                reportString = "FST";
                break;
            case LXT2_IS_FSDB:
                reportString = "FSDB";
                break;
            default:
                break;
        }
    }

    aobj = Tcl_NewStringObj(reportString, -1);
    Tcl_SetObjResult(interp, aobj);

    return (TCL_OK);
}

static int gtkwavetcl_getNamedMarker(ClientData clientData,
                                     Tcl_Interp *interp,
                                     int objc,
                                     Tcl_Obj *CONST objv[])
{
    if (objc == 2) {
        char *s = get_Tcl_string(objv[1]);
        int which = -1;

        if ((s[0] >= 'A') && (s[0] <= 'Z')) {
            which = bijective_marker_id_string_hash(s);
        } else if ((s[0] >= 'a') && (s[0] <= 'z')) {
            which = bijective_marker_id_string_hash(s);
        } else {
            which = atoi(s);
        }

        if ((which >= 0) && (which < WAVE_NUM_NAMED_MARKERS)) {
            TimeType value = GLOBALS->named_markers[which];
            return (gtkwavetcl_printTimeType(clientData, interp, objc, objv, value));
        }
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    return (TCL_OK);
}

static int gtkwavetcl_getWaveHeight(ClientData clientData,
                                    Tcl_Interp *interp,
                                    int objc,
                                    Tcl_Obj *CONST objv[])
{
    int value = GLOBALS->waveheight;
    return (gtkwavetcl_printInteger(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getWaveWidth(ClientData clientData,
                                   Tcl_Interp *interp,
                                   int objc,
                                   Tcl_Obj *CONST objv[])
{
    int value = GLOBALS->wavewidth;
    return (gtkwavetcl_printInteger(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getPixelsUnitTime(ClientData clientData,
                                        Tcl_Interp *interp,
                                        int objc,
                                        Tcl_Obj *CONST objv[])
{
    double value = GLOBALS->pxns;
    return (gtkwavetcl_printDouble(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getUnitTimePixels(ClientData clientData,
                                        Tcl_Interp *interp,
                                        int objc,
                                        Tcl_Obj *CONST objv[])
{
    double value = GLOBALS->nspx;
    return (gtkwavetcl_printDouble(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getZoomFactor(ClientData clientData,
                                    Tcl_Interp *interp,
                                    int objc,
                                    Tcl_Obj *CONST objv[])
{
    double value = GLOBALS->tims.zoom;
    return (gtkwavetcl_printDouble(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getDumpFileName(ClientData clientData,
                                      Tcl_Interp *interp,
                                      int objc,
                                      Tcl_Obj *CONST objv[])
{
    char *value = GLOBALS->loaded_file_name;
    return (gtkwavetcl_printString(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getVisibleNumTraces(ClientData clientData,
                                          Tcl_Interp *interp,
                                          int objc,
                                          Tcl_Obj *CONST objv[])
{
    int value = GLOBALS->traces.visible;
    return (gtkwavetcl_printInteger(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getTotalNumTraces(ClientData clientData,
                                        Tcl_Interp *interp,
                                        int objc,
                                        Tcl_Obj *CONST objv[])
{
    int value = GLOBALS->traces.total;
    return (gtkwavetcl_printInteger(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getTraceNameFromIndex(ClientData clientData,
                                            Tcl_Interp *interp,
                                            int objc,
                                            Tcl_Obj *CONST objv[])
{
    if (objc == 2) {
        char *s = get_Tcl_string(objv[1]);
        int which = atoi(s);

        if ((which >= 0) && (which < GLOBALS->traces.total)) {
            Trptr t = GLOBALS->traces.first;
            int i = 0;
            while (t) {
                if (i == which) {
                    if (t->name) {
                        return (gtkwavetcl_printString(clientData, interp, objc, objv, t->name));
                    } else {
                        break;
                    }
                }

                i++;
                t = t->t_next;
            }
        }
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 2));
    }

    return (TCL_OK);
}

static int gtkwavetcl_getTraceFlagsFromIndex(ClientData clientData,
                                             Tcl_Interp *interp,
                                             int objc,
                                             Tcl_Obj *CONST objv[])
{
    if (objc == 2) {
        char *s = get_Tcl_string(objv[1]);
        int which = atoi(s);

        if ((which >= 0) && (which < GLOBALS->traces.total)) {
            Trptr t = GLOBALS->traces.first;
            int i = 0;
            while (t) {
                if (i == which) {
                    return (
                        gtkwavetcl_printTraceFlagsType(clientData, interp, objc, objv, t->flags));
                }

                i++;
                t = t->t_next;
            }
        }
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    return (TCL_OK);
}

static int gtkwavetcl_getTraceValueAtMarkerFromIndex(ClientData clientData,
                                                     Tcl_Interp *interp,
                                                     int objc,
                                                     Tcl_Obj *CONST objv[])
{
    if (objc == 2) {
        char *s = get_Tcl_string(objv[1]);
        int which = atoi(s);

        if ((which >= 0) && (which < GLOBALS->traces.total)) {
            Trptr t = GLOBALS->traces.first;
            int i = 0;
            while (t) {
                if (i == which) {
                    if (t->asciivalue) {
                        char *pnt = t->asciivalue;
                        if (*pnt == '=')
                            pnt++;

                        return (gtkwavetcl_printString(clientData, interp, objc, objv, pnt));
                    } else {
                        break;
                    }
                }

                i++;
                t = t->t_next;
            }
        }
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    return (TCL_OK);
}

static int gtkwavetcl_getTraceValueAtMarkerFromName(ClientData clientData,
                                                    Tcl_Interp *interp,
                                                    int objc,
                                                    Tcl_Obj *CONST objv[])
{
    if (objc == 2) {
        char *s = get_Tcl_string(objv[1]);
        Trptr t = GLOBALS->traces.first;

        while (t) {
            if (!(t->flags & (TR_BLANK | TR_ANALOG_BLANK_STRETCH))) {
                char *name = extractFullTraceName(t);
                if (!strcmp(name, s)) {
                    free_2(name);
                    break;
                }
                free_2(name);
            }
            t = t->t_next;
        }

        if (t) {
            if (t->asciivalue) {
                char *pnt = t->asciivalue;
                if (*pnt == '=')
                    pnt++;
                return (gtkwavetcl_printString(clientData, interp, objc, objv, pnt));
            }
        }
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    return (TCL_OK);
}

static int gtkwavetcl_getTraceValueAtNamedMarkerFromName(ClientData clientData,
                                                         Tcl_Interp *interp,
                                                         int objc,
                                                         Tcl_Obj *CONST objv[])
{
    if (objc == 3) {
        char *sv = get_Tcl_string(objv[1]);
        int which = -1;
        TimeType oldmarker = GLOBALS->tims.marker;
        TimeType value = LLDescriptor(-1);

        if ((sv[0] >= 'A') && (sv[0] <= 'Z')) {
            which = bijective_marker_id_string_hash(sv);
        } else if ((sv[0] >= 'a') && (sv[0] <= 'z')) {
            which = bijective_marker_id_string_hash(sv);
        } else {
            which = atoi(sv);
        }

        if ((which >= 0) && (which < WAVE_NUM_NAMED_MARKERS)) {
            char *s = get_Tcl_string(objv[2]);
            Trptr t = GLOBALS->traces.first;

            value = GLOBALS->named_markers[which];

            while (t) {
                if (!(t->flags & (TR_BLANK | TR_ANALOG_BLANK_STRETCH))) {
                    char *name = extractFullTraceName(t);
                    if (!strcmp(name, s)) {
                        free_2(name);
                        break;
                    }
                    free_2(name);
                }
                t = t->t_next;
            }

            if (t && (value >= LLDescriptor(0))) {
                GLOBALS->tims.marker = value;
                GLOBALS->signalwindow_width_dirty = 1;
                redraw_signals_and_waves();
                gtkwave_main_iteration();

                if (t->asciivalue) {
                    Tcl_Obj *aobj;
                    char *pnt = t->asciivalue;
                    if (*pnt == '=')
                        pnt++;

                    aobj = Tcl_NewStringObj(pnt, -1);
                    Tcl_SetObjResult(interp, aobj);

                    GLOBALS->tims.marker = oldmarker;
                    update_time_box();
                    GLOBALS->signalwindow_width_dirty = 1;
                    redraw_signals_and_waves();
                    gtkwave_main_iteration();

                    return (TCL_OK);
                }

                GLOBALS->tims.marker = oldmarker;
                update_time_box();
                GLOBALS->signalwindow_width_dirty = 1;
                redraw_signals_and_waves();
                gtkwave_main_iteration();
            }
        }
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    return (TCL_OK);
}

static int gtkwavetcl_getHierMaxLevel(ClientData clientData,
                                      Tcl_Interp *interp,
                                      int objc,
                                      Tcl_Obj *CONST objv[])
{
    int value = GLOBALS->hier_max_level;
    return (gtkwavetcl_printInteger(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getFontHeight(ClientData clientData,
                                    Tcl_Interp *interp,
                                    int objc,
                                    Tcl_Obj *CONST objv[])
{
    int value = GLOBALS->fontheight;
    return (gtkwavetcl_printInteger(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getLeftJustifySigs(ClientData clientData,
                                         Tcl_Interp *interp,
                                         int objc,
                                         Tcl_Obj *CONST objv[])
{
    int value = (GLOBALS->left_justify_sigs != 0);
    return (gtkwavetcl_printInteger(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_getSaveFileName(ClientData clientData,
                                      Tcl_Interp *interp,
                                      int objc,
                                      Tcl_Obj *CONST objv[])
{
    char *value = GLOBALS->filesel_writesave;
    if (value) {
        return (gtkwavetcl_printString(clientData, interp, objc, objv, value));
    }

    return (TCL_OK);
}

static int gtkwavetcl_getStemsFileName(ClientData clientData,
                                       Tcl_Interp *interp,
                                       int objc,
                                       Tcl_Obj *CONST objv[])
{
    char *value = GLOBALS->stems_name;
    if (value) {
        return (gtkwavetcl_printString(clientData, interp, objc, objv, value));
    }

    return (TCL_OK);
}

static int gtkwavetcl_getTraceScrollbarRowValue(ClientData clientData,
                                                Tcl_Interp *interp,
                                                int objc,
                                                Tcl_Obj *CONST objv[])
{
    GtkAdjustment *wadj = gw_signal_list_get_vadjustment(GW_SIGNAL_LIST(GLOBALS->signalarea));
    int value = (int)gtk_adjustment_get_value(wadj);

    return (gtkwavetcl_printInteger(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_setMarker(ClientData clientData,
                                Tcl_Interp *interp,
                                int objc,
                                Tcl_Obj *CONST objv[])
{
    if (objc == 2) {
        char *s = get_Tcl_string(objv[1]);
        TimeType mrk = unformat_time(s, GLOBALS->time_dimension);

        if ((mrk >= GLOBALS->min_time) && (mrk <= GLOBALS->max_time)) {
            GLOBALS->tims.marker = mrk;
        } else {
            GLOBALS->tims.marker = LLDescriptor(-1);
        }

        update_time_box();
        GLOBALS->signalwindow_width_dirty = 1;
        redraw_signals_and_waves();

        gtkwave_main_iteration();
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    return (TCL_OK);
}

static int gtkwavetcl_setBaselineMarker(ClientData clientData,
                                        Tcl_Interp *interp,
                                        int objc,
                                        Tcl_Obj *CONST objv[])
{
    if (objc == 2) {
        char *s = get_Tcl_string(objv[1]);
        TimeType mrk = unformat_time(s, GLOBALS->time_dimension);

        if ((mrk >= GLOBALS->min_time) && (mrk <= GLOBALS->max_time)) {
            GLOBALS->tims.baseline = mrk;
        } else {
            GLOBALS->tims.baseline = LLDescriptor(-1);
        }

        update_time_box();
        GLOBALS->signalwindow_width_dirty = 1;
        redraw_signals_and_waves();

        gtkwave_main_iteration();
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    return (TCL_OK);
}

static int gtkwavetcl_setWindowStartTime(ClientData clientData,
                                         Tcl_Interp *interp,
                                         int objc,
                                         Tcl_Obj *CONST objv[])
{
    if (objc == 2) {
        char *s = get_Tcl_string(objv[1]);

        if (s) {
            TimeType gt;
            char timval[40];
            GtkAdjustment *hadj;
            TimeType pageinc;

            gt = unformat_time(s, GLOBALS->time_dimension);

            if (gt < GLOBALS->tims.first)
                gt = GLOBALS->tims.first;
            else if (gt > GLOBALS->tims.last)
                gt = GLOBALS->tims.last;

            hadj = GTK_ADJUSTMENT(GLOBALS->wave_hslider);
            gtk_adjustment_set_value(hadj, gt);

            pageinc = (TimeType)(((gdouble)GLOBALS->wavewidth) * GLOBALS->nspx);
            if (gt < (GLOBALS->tims.last - pageinc + 1))
                GLOBALS->tims.timecache = gt;
            else {
                GLOBALS->tims.timecache = GLOBALS->tims.last - pageinc + 1;
                if (GLOBALS->tims.timecache < GLOBALS->tims.first)
                    GLOBALS->tims.timecache = GLOBALS->tims.first;
            }

            reformat_time(timval, GLOBALS->tims.timecache, GLOBALS->time_dimension);

            time_update();
        }

        redraw_signals_and_waves();
        gtkwave_main_iteration();
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    return (TCL_OK);
}

static int gtkwavetcl_setZoomFactor(ClientData clientData,
                                    Tcl_Interp *interp,
                                    int objc,
                                    Tcl_Obj *CONST objv[])
{
    if (objc == 2) {
        char *s = get_Tcl_string(objv[1]);
        float f;

        sscanf(s, "%f", &f);
        if (f > 0.0) {
            f = 0.0; /* in case they try to go out of range */
        } else if (f < -62.0) {
            f = -62.0; /* in case they try to go out of range */
        }

        GLOBALS->tims.prevzoom = GLOBALS->tims.zoom;
        GLOBALS->tims.zoom = (gdouble)f;
        calczoom(GLOBALS->tims.zoom);
        fix_wavehadj();

        g_signal_emit_by_name(GTK_ADJUSTMENT(GLOBALS->wave_hslider), "changed");
        g_signal_emit_by_name(GTK_ADJUSTMENT(GLOBALS->wave_hslider), "value_changed");

        gtkwave_main_iteration();
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    return (TCL_OK);
}

static int gtkwavetcl_setZoomRangeTimes(ClientData clientData,
                                        Tcl_Interp *interp,
                                        int objc,
                                        Tcl_Obj *CONST objv[])
{
    if (objc == 3) {
        char *s, *t;
        TimeType time1, time2;
        TimeType oldmarker = GLOBALS->tims.marker;

        s = get_Tcl_string(objv[1]);
        time1 = unformat_time(s, GLOBALS->time_dimension);
        t = get_Tcl_string(objv[2]);
        time2 = unformat_time(t, GLOBALS->time_dimension);

        if (time1 < GLOBALS->tims.first) {
            time1 = GLOBALS->tims.first;
        }
        if (time1 > GLOBALS->tims.last) {
            time1 = GLOBALS->tims.last;
        }
        if (time2 < GLOBALS->tims.first) {
            time2 = GLOBALS->tims.first;
        }
        if (time2 > GLOBALS->tims.last) {
            time2 = GLOBALS->tims.last;
        }

        service_dragzoom(time1, time2);
        GLOBALS->tims.marker = oldmarker;

        GLOBALS->signalwindow_width_dirty = 1;
        redraw_signals_and_waves();

        gtkwave_main_iteration();
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    return (TCL_OK);
}

static int gtkwavetcl_setLeftJustifySigs(ClientData clientData,
                                         Tcl_Interp *interp,
                                         int objc,
                                         Tcl_Obj *CONST objv[])
{
    if (objc == 2) {
        char *s = get_Tcl_string(objv[1]);
        TimeType val = atoi_64(s);
        GLOBALS->left_justify_sigs = (val != LLDescriptor(0)) ? ~0 : 0;

        MaxSignalLength();
        gw_signal_list_force_redraw(GW_SIGNAL_LIST(GLOBALS->signalarea));

        gtkwave_main_iteration();
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    return (TCL_OK);
}

static int gtkwavetcl_setNamedMarker(ClientData clientData,
                                     Tcl_Interp *interp,
                                     int objc,
                                     Tcl_Obj *CONST objv[])
{
    if ((objc == 3) || (objc == 4)) {
        char *s = get_Tcl_string(objv[1]);
        int which = -1;

        if ((s[0] >= 'A') && (s[0] <= 'Z')) {
            which = bijective_marker_id_string_hash(s);
        } else if ((s[0] >= 'a') && (s[0] <= 'z')) {
            which = bijective_marker_id_string_hash(s);
        } else {
            which = atoi(s);
        }

        if ((which >= 0) && (which < WAVE_NUM_NAMED_MARKERS)) {
            char *t = get_Tcl_string(objv[2]);
            TimeType gt = unformat_time(t, GLOBALS->time_dimension);

            GLOBALS->named_markers[which] = gt;

            if (GLOBALS->marker_names[which]) {
                free_2(GLOBALS->marker_names[which]);
                GLOBALS->marker_names[which] = NULL;
            }

            if (objc == 4) {
                char *u = get_Tcl_string(objv[3]);

                GLOBALS->marker_names[which] = strdup_2(u);
            }

            gw_wave_view_force_redraw(GW_WAVE_VIEW(GLOBALS->wavearea));
            gtkwave_main_iteration();
        }
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 2));
    }

    return (TCL_OK);
}

static int gtkwavetcl_setTraceScrollbarRowValue(ClientData clientData,
                                                Tcl_Interp *interp,
                                                int objc,
                                                Tcl_Obj *CONST objv[])
{
    if (objc == 2) {
        char *s = get_Tcl_string(objv[1]);
        int target = atoi(s);

        SetTraceScrollbarRowValue(target, 0);

        /*         GtkAdjustment *wadj=GTK_ADJUSTMENT(GLOBALS->wave_vslider); */

        /*         int
         * num_traces_displayable=(GLOBALS->signalarea->allocation.height)/(GLOBALS->fontheight); */
        /*         num_traces_displayable--;   /\* for the time trace that is always there *\/ */

        /* 	if(target > GLOBALS->traces.visible - num_traces_displayable) target =
         * GLOBALS->traces.visible - num_traces_displayable; */

        /* 	if(target < 0) target = 0; */

        /* 	wadj->value = target; */

        /*         g_signal_emit_by_name (wadj, "changed"); /\* force bar update *\/ */
        /*         g_signal_emit_by_name (wadj, "value_changed"); /\* force text update *\/ */
        /*	gtkwave_main_iteration(); */
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    return (TCL_OK);
}

static int gtkwavetcl_addCommentTracesFromList(ClientData clientData,
                                               Tcl_Interp *interp,
                                               int objc,
                                               Tcl_Obj *CONST objv[])
{
    char reportString[33];
    Tcl_Obj *aobj;

    if (objc == 2) {
        char *s = Tcl_GetString(objv[1]);
        char **elem = NULL;
        int i, l = 0;

        elem = zSplitTclList(s, &l);
        if (elem) {
            for (i = 0; i < l; i++) {
                InsertBlankTrace(elem[i], 0);
            }
            free_2(elem);
        }

        GLOBALS->signalwindow_width_dirty = 1;
        redraw_signals_and_waves();

        sprintf(reportString, "%d", l);

        aobj = Tcl_NewStringObj(reportString, -1);
        Tcl_SetObjResult(interp, aobj);
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    return (TCL_OK);
}

static int gtkwavetcl_addSignalsFromList(ClientData clientData,
                                         Tcl_Interp *interp,
                                         int objc,
                                         Tcl_Obj *CONST objv[])
{
    int i;
    char *one_entry = NULL, *mult_entry = NULL;
    unsigned int mult_len = 0;
    int num_found = 0;
    char reportString[33];
    Tcl_Obj *aobj;

    if (objc == 2) {
        char *s = Tcl_GetString(objv[1]); /* do not want to remove braces! */
        char **elem = NULL;
        int l = 0;

        elem = zSplitTclList(s, &l);

        if (elem) {
            for (i = 0; i < l; i++) {
                one_entry = make_single_tcl_list_name(elem[i], NULL, 0, 1); /* keep range */
                WAVE_OE_ME
            }
            free_2(elem);
            elem = NULL;
            if (mult_entry) {
                num_found = process_tcl_list(mult_entry, FALSE);
                free_2(mult_entry);
            }
            if (num_found) {
                redraw_signals_and_waves();
                gtkwave_main_iteration();
            }
        }
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    sprintf(reportString, "%d", num_found);

    aobj = Tcl_NewStringObj(reportString, -1);
    Tcl_SetObjResult(interp, aobj);

    return (TCL_OK);
}

static int gtkwavetcl_processTclList(ClientData clientData,
                                     Tcl_Interp *interp,
                                     int objc,
                                     Tcl_Obj *CONST objv[])
{
    int num_found = 0;
    char reportString[33];
    Tcl_Obj *aobj;

    if (objc == 2) {
        char *s = Tcl_GetString(objv[1]); /* do not want to remove braces! */
        if (s) {
            num_found = process_tcl_list(s, FALSE);
            if (num_found) {
                redraw_signals_and_waves();
                gtkwave_main_iteration();
            }
        }
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    sprintf(reportString, "%d", num_found);

    aobj = Tcl_NewStringObj(reportString, -1);
    Tcl_SetObjResult(interp, aobj);

    return (TCL_OK);
}

static int gtkwavetcl_deleteSignalsFromList(ClientData clientData,
                                            Tcl_Interp *interp,
                                            int objc,
                                            Tcl_Obj *CONST objv[])
{
    int i;
    int num_found = 0;
    char reportString[33];
    Tcl_Obj *aobj;

    if (objc == 2) {
        char *s = Tcl_GetString(objv[1]); /* do not want to remove braces */
        char **elem = NULL;
        int l = 0;

        elem = zSplitTclList(s, &l);

        if (elem) {
            Trptr t = GLOBALS->traces.first;
            while (t) {
                t->cached_flags = t->flags;
                t->flags &= (~TR_HIGHLIGHT);
                t = t->t_next;
            }

            for (i = 0; i < l; i++) {
                t = GLOBALS->traces.first;
                while (t) {
                    if (!(t->flags & (TR_BLANK | TR_ANALOG_BLANK_STRETCH | TR_HIGHLIGHT))) {
                        char *name = extractFullTraceName(t);
                        if (name) {
                            int len_name = strlen(name);
                            int len_elem = strlen(elem[i]);
                            int brackmatch = (len_name > len_elem) && (name[len_elem] == '[');

                            if (((len_name == len_elem) && (!strcmp(name, elem[i]))) ||
                                (brackmatch && !strncmp(name, elem[i], len_elem))) {
                                t->flags |= TR_HIGHLIGHT;
                                num_found++;
                                break;
                            }
                            free_2(name);
                        }
                    }
                    t = t->t_next;
                }
            }

            free_2(elem);
            elem = NULL;

            if (num_found) {
                CutBuffer();
            }

            t = GLOBALS->traces.first;
            while (t) {
                t->flags = t->cached_flags;
                t->cached_flags = 0;
                t = t->t_next;
            }

            if (num_found) {
                redraw_signals_and_waves();
                gtkwave_main_iteration();
            }
        }
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    sprintf(reportString, "%d", num_found);

    aobj = Tcl_NewStringObj(reportString, -1);
    Tcl_SetObjResult(interp, aobj);

    return (TCL_OK);
}

static int gtkwavetcl_deleteSignalsFromListIncludingDuplicates(ClientData clientData,
                                                               Tcl_Interp *interp,
                                                               int objc,
                                                               Tcl_Obj *CONST objv[])
{
    int i;
    int num_found = 0;
    char reportString[33];
    Tcl_Obj *aobj;

    if (objc == 2) {
        char *s = Tcl_GetString(objv[1]); /* do not want to remove braces */
        char **elem = NULL;
        int l = 0;

        elem = zSplitTclList(s, &l);

        if (elem) {
            Trptr t = GLOBALS->traces.first;
            while (t) {
                t->cached_flags = t->flags;
                t->flags &= (~TR_HIGHLIGHT);

                if (!(t->flags & (TR_BLANK | TR_ANALOG_BLANK_STRETCH))) {
                    char *name = extractFullTraceName(t);
                    if (name) {
                        for (i = 0; i < l; i++) {
                            int len_name = strlen(name);
                            int len_elem = strlen(elem[i]);
                            int brackmatch = (len_name > len_elem) && (name[len_elem] == '[');

                            if (((len_name == len_elem) && (!strcmp(name, elem[i]))) ||
                                (brackmatch && !strncmp(name, elem[i], len_elem))) {
                                t->flags |= TR_HIGHLIGHT;
                                num_found++;
                                break;
                            }
                        }
                        free_2(name);
                    }
                }

                t = t->t_next;
            }

            free_2(elem);
            elem = NULL;

            if (num_found) {
                CutBuffer();
            }

            t = GLOBALS->traces.first;
            while (t) {
                t->flags = t->cached_flags;
                t->cached_flags = 0;
                t = t->t_next;
            }

            if (num_found) {
                redraw_signals_and_waves();
                gtkwave_main_iteration();
            }
        }
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    sprintf(reportString, "%d", num_found);

    aobj = Tcl_NewStringObj(reportString, -1);
    Tcl_SetObjResult(interp, aobj);

    return (TCL_OK);
}

static int gtkwavetcl_highlightSignalsFromList(ClientData clientData,
                                               Tcl_Interp *interp,
                                               int objc,
                                               Tcl_Obj *CONST objv[])
{
    int i;
    int num_found = 0;
    char reportString[33];
    Tcl_Obj *aobj;

    if (objc == 2) {
        char *s = Tcl_GetString(objv[1]); /* do not want to remove braces */
        char **elem = NULL;
        int l = 0;

        elem = zSplitTclList(s, &l);

        if (elem) {
            Trptr t = GLOBALS->traces.first;
            while (t) {
                if (!(t->flags & (TR_BLANK | TR_ANALOG_BLANK_STRETCH))) {
                    char *name = extractFullTraceName(t);
                    if (name) {
                        for (i = 0; i < l; i++) {
                            if (!strcmp(name, elem[i])) {
                                t->flags |= TR_HIGHLIGHT;
                                num_found++;
                                break;
                            }
                        }
                        free_2(name);
                    }
                }

                t = t->t_next;
            }

            free_2(elem);
            elem = NULL;

            if (num_found) {
                redraw_signals_and_waves();
                gtkwave_main_iteration();
            }
        }
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    sprintf(reportString, "%d", num_found);

    aobj = Tcl_NewStringObj(reportString, -1);
    Tcl_SetObjResult(interp, aobj);

    return (TCL_OK);
}

static int gtkwavetcl_unhighlightSignalsFromList(ClientData clientData,
                                                 Tcl_Interp *interp,
                                                 int objc,
                                                 Tcl_Obj *CONST objv[])
{
    int i;
    int num_found = 0;
    char reportString[33];
    Tcl_Obj *aobj;

    if (objc == 2) {
        char *s = Tcl_GetString(objv[1]); /* do not want to remove braces */
        char **elem = NULL;
        int l = 0;

        elem = zSplitTclList(s, &l);

        if (elem) {
            Trptr t = GLOBALS->traces.first;
            while (t) {
                if (!(t->flags & (TR_BLANK | TR_ANALOG_BLANK_STRETCH))) {
                    char *name = extractFullTraceName(t);
                    if (name) {
                        for (i = 0; i < l; i++) {
                            if (!strcmp(name, elem[i])) {
                                t->flags &= (~TR_HIGHLIGHT);
                                num_found++;
                                break;
                            }
                        }
                        free_2(name);
                    }
                }

                t = t->t_next;
            }

            free_2(elem);
            elem = NULL;

            if (num_found) {
                redraw_signals_and_waves();
                gtkwave_main_iteration();
            }
        }
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    sprintf(reportString, "%d", num_found);

    aobj = Tcl_NewStringObj(reportString, -1);
    Tcl_SetObjResult(interp, aobj);

    return (TCL_OK);
}

static int gtkwavetcl_setTraceHighlightFromIndex(ClientData clientData,
                                                 Tcl_Interp *interp,
                                                 int objc,
                                                 Tcl_Obj *CONST objv[])
{
    if (objc == 3) {
        char *s = get_Tcl_string(objv[1]);
        int which = atoi(s);
        char *ts = get_Tcl_string(objv[2]);
        int onoff = atoi_64(ts);

        if ((which >= 0) && (which < GLOBALS->traces.total)) {
            Trptr t = GLOBALS->traces.first;
            int i = 0;
            while (t) {
                if (i == which) {
                    if (onoff) {
                        t->flags |= TR_HIGHLIGHT;
                    } else {
                        t->flags &= (~TR_HIGHLIGHT);
                    }
                    gw_signal_list_force_redraw(GW_SIGNAL_LIST(GLOBALS->signalarea));
                    gtkwave_main_iteration();
                    break;
                }

                i++;
                t = t->t_next;
            }
        }
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 2));
    }

    return (TCL_OK);
}

static int gtkwavetcl_setTraceHighlightFromNameMatch(ClientData clientData,
                                                     Tcl_Interp *interp,
                                                     int objc,
                                                     Tcl_Obj *CONST objv[])
{
    if (objc == 3) {
        char *s = get_Tcl_string(objv[1]);
        int which = atoi(s);
        char *ts = get_Tcl_string(objv[2]);
        int onoff = atoi_64(ts);
        int mat = 0;

        if ((which >= 0) && (which < GLOBALS->traces.total)) {
            Trptr t = GLOBALS->traces.first;
            while (t) {
                if (t->name && !strcmp(t->name, s)) {
                    if (onoff) {
                        t->flags |= TR_HIGHLIGHT;
                    } else {
                        t->flags &= (~TR_HIGHLIGHT);
                    }
                    mat++;
                }

                t = t->t_next;
            }

            if (mat) {
                gw_signal_list_force_redraw(GW_SIGNAL_LIST(GLOBALS->signalarea));
                gtkwave_main_iteration();
            }

            return (gtkwavetcl_printInteger(clientData, interp, objc, objv, mat));
        }
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 2));
    }

    return (TCL_OK);
}

static int gtkwavetcl_signalChangeList(ClientData clientData,
                                       Tcl_Interp *interp,
                                       int objc,
                                       Tcl_Obj *CONST objv[])
{
    (void)clientData;

    int dir = STRACE_FORWARD;
    TimeType start_time = 0;
    TimeType end_time = MAX_HISTENT_TIME;
    int max_elements = 0x7fffffff;
    char *sig_name = NULL;
    int i;
    char *str_p, *str1_p;
    int error = 0;
    llist_p *l_head;
    Tcl_Obj *l_obj;
    Tcl_Obj *obj;
    llist_p *p, *p1;

    for (i = 1; i < objc; i++) {
        str_p = Tcl_GetStringFromObj(objv[i], NULL);
        if (*str_p != '-') {
            sig_name = str_p;
        } else {
            if (i == (objc - 1)) { /* loop overflow on i check */
                error++;
                break;
            }
            str1_p = Tcl_GetStringFromObj(objv[++i], NULL);
            switch (str_p[1]) {
                case 's': /* start time */
                    if (!strstr("-start_time", str_p))
                        error++;
                    else {
                        if ((start_time = atoi_64(str1_p)) < 0)
                            start_time = 0;
                        dir = (start_time > end_time) ? STRACE_BACKWARD : STRACE_FORWARD;
                    }
                    break;
                case 'e': /* end time */
                    if (!strstr("-end_time", str_p))
                        error++;
                    else {
                        end_time = atoi_64(str1_p);
                        dir = (start_time > end_time) ? STRACE_BACKWARD : STRACE_FORWARD;
                    }
                    break;
                case 'm': /* max */
                    if (!strstr("-max", str_p))
                        error++;
                    else {
                        max_elements = atoi(str1_p);
                    }
                    break;
                case 'd': /* dir */
                    if (!strstr("-dir", str_p))
                        error++;
                    else {
                        if (strstr("forward", str1_p))
                            dir = STRACE_FORWARD;
                        else if (strstr("backward", str1_p))
                            dir = STRACE_BACKWARD;
                        else
                            error++;
                    }
                    break;
                default:
                    error++;
            }
        }
    }
    /* consistancy check */
    if (dir == STRACE_FORWARD) {
        if (start_time > end_time)
            error++;
    } else {
        if (start_time < end_time) {
            if (end_time == MAX_HISTENT_TIME)
                end_time = 0;
            else
                error++;
        }
    }
    if (error) {
        Tcl_SetObjResult(interp,
                         Tcl_NewStringObj("Usage: signal_change_list ?name? ?-start time? ?-end "
                                          "time? ?-max size? ?-dir forward|backward?",
                                          -1));
        return TCL_ERROR;
    }
    l_head = signal_change_list(sig_name, dir, start_time, end_time, max_elements);
    l_obj = Tcl_NewListObj(0, NULL);
    p = l_head;
    while (p) {
        obj = Tcl_NewWideIntObj((Tcl_WideInt)p->u.tt);
        p1 = p->next;
        free_2(p);
        Tcl_ListObjAppendElement(interp, l_obj, obj);
        obj = Tcl_NewStringObj(p1->u.str, -1);
        Tcl_ListObjAppendElement(interp, l_obj, obj);
        p = p1->next;
        free_2(p1->u.str);
        free_2(p1);
    }
    Tcl_SetObjResult(interp, l_obj);
    return TCL_OK;
}

static int gtkwavetcl_findNextEdge(ClientData clientData,
                                   Tcl_Interp *interp,
                                   int objc,
                                   Tcl_Obj *CONST objv[])
{
    edge_search(STRACE_FORWARD);
    gtkwave_main_iteration();
    return (gtkwavetcl_getMarker(clientData, interp, objc, objv));
}

static int gtkwavetcl_findPrevEdge(ClientData clientData,
                                   Tcl_Interp *interp,
                                   int objc,
                                   Tcl_Obj *CONST objv[])
{
    edge_search(STRACE_BACKWARD);
    gtkwave_main_iteration();
    return (gtkwavetcl_getMarker(clientData, interp, objc, objv));
}

int SST_open_node(char *name);
static int gtkwavetcl_forceOpenTreeNode(ClientData clientData,
                                        Tcl_Interp *interp,
                                        int objc,
                                        Tcl_Obj *CONST objv[])
{
    int rv = -100; /* Tree does not exist */
    char *s = NULL;
    if (objc == 2)
        s = get_Tcl_string(objv[1]);

    if (s && (strlen(s) > 1)) { /* exclude empty strings */
        int len = strlen(s);
        if (s[len - 1] != GLOBALS->hier_delimeter) {
            rv = SST_open_node(s);
        } else {
            rv = SST_open_node(s);
        }
    } else {
        if (GLOBALS->selected_hierarchy_name) {
            rv = SST_NODE_CURRENT;
        }
        gtkwave_main_iteration(); /* check if this is needed */
    }
    if (rv == -100) {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }
    Tcl_SetObjResult(GLOBALS->interp,
                     (rv == SST_NODE_CURRENT)
                         ? Tcl_NewStringObj(GLOBALS->selected_hierarchy_name,
                                            strlen(GLOBALS->selected_hierarchy_name))
                         : Tcl_NewIntObj(rv));

    return (TCL_OK);
}

static int gtkwavetcl_setFromEntry(ClientData clientData,
                                   Tcl_Interp *interp,
                                   int objc,
                                   Tcl_Obj *CONST objv[])
{
    if (objc == 2) {
        char *s = get_Tcl_string(objv[1]);

        if (s) {
            gtk_entry_set_text(GTK_ENTRY(GLOBALS->from_entry), s);
            from_entry_callback(NULL, GLOBALS->from_entry);
        }

        gtkwave_main_iteration();
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    return (TCL_OK);
}

static int gtkwavetcl_setToEntry(ClientData clientData,
                                 Tcl_Interp *interp,
                                 int objc,
                                 Tcl_Obj *CONST objv[])
{
    if (objc == 2) {
        char *s = get_Tcl_string(objv[1]);

        if (s) {
            gtk_entry_set_text(GTK_ENTRY(GLOBALS->to_entry), s);
            to_entry_callback(NULL, GLOBALS->to_entry);
        }

        gtkwave_main_iteration();
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    return (TCL_OK);
}

static int gtkwavetcl_getFromEntry(ClientData clientData,
                                   Tcl_Interp *interp,
                                   int objc,
                                   Tcl_Obj *CONST objv[])
{
    const char *value = gtk_entry_get_text(GTK_ENTRY(GLOBALS->from_entry));
    if (value) {
        return (gtkwavetcl_printString(clientData, interp, objc, objv, value));
    }

    return (TCL_OK);
}

static int gtkwavetcl_getToEntry(ClientData clientData,
                                 Tcl_Interp *interp,
                                 int objc,
                                 Tcl_Obj *CONST objv[])
{
    const char *value = gtk_entry_get_text(GTK_ENTRY(GLOBALS->to_entry));
    if (value) {
        return (gtkwavetcl_printString(clientData, interp, objc, objv, value));
    }

    return (TCL_OK);
}

static int gtkwavetcl_getDisplayedSignals(ClientData clientData,
                                          Tcl_Interp *interp,
                                          int objc,
                                          Tcl_Obj *CONST objv[])
{
    if (objc == 1) {
        char *rv = add_traces_from_signal_window(TRUE);
        int rc = gtkwavetcl_printString(clientData, interp, objc, objv, rv);

        free_2(rv);
        return (rc);
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }
}

static int gtkwavetcl_getTraceFlagsFromName(ClientData clientData,
                                            Tcl_Interp *interp,
                                            int objc,
                                            Tcl_Obj *CONST objv[])
{
    if (objc == 2) {
        char *s = get_Tcl_string(objv[1]);
        Trptr t = GLOBALS->traces.first;

        while (t) {
            if (!(t->flags & (TR_BLANK | TR_ANALOG_BLANK_STRETCH))) {
                char *name = extractFullTraceName(t);
                if (!strcmp(name, s)) {
                    free_2(name);
                    break;
                }
                free_2(name);
            }
            t = t->t_next;
        }

        if (t) {
            return (gtkwavetcl_printTraceFlagsType(clientData, interp, objc, objv, t->flags));
        }
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    return (TCL_OK);
}

static int gtkwavetcl_loadFile(ClientData clientData,
                               Tcl_Interp *interp,
                               int objc,
                               Tcl_Obj *CONST objv[])
{
    if (objc == 2) {
        char *s = get_Tcl_string(objv[1]);

        if (!GLOBALS->in_tcl_callback) {
            /* wave_gconf_client_set_string("/current/savefile", s); */
            /*	read_save_helper(s, NULL, NULL, NULL, NULL); */
            process_url_file(s);
            /*	process_url_list(s); */
            /*	gtkwave_main_iteration(); */
        } else {
            gtkwavetcl_setvar_nonblocking(WAVE_TCLCB_ERROR,
                                          "gtkwave::loadFile prohibited in callback",
                                          WAVE_TCLCB_ERROR_FLAGS);
        }
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }

    return (TCL_OK);
}

static int gtkwavetcl_reLoadFile(ClientData clientData,
                                 Tcl_Interp *interp,
                                 int objc,
                                 Tcl_Obj *CONST objv[])
{
    if (objc == 1) {
        if (!GLOBALS->in_tcl_callback) {
            reload_into_new_context();
        } else {
            gtkwavetcl_setvar_nonblocking(WAVE_TCLCB_ERROR,
                                          "gtkwave::reLoadFile prohibited in callback",
                                          WAVE_TCLCB_ERROR_FLAGS);
        }
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 0));
    }
    return (TCL_OK);
}

static int gtkwavetcl_presentWindow(ClientData clientData,
                                    Tcl_Interp *interp,
                                    int objc,
                                    Tcl_Obj *CONST objv[])
{
    if (objc == 1) {
        gtk_window_present(GTK_WINDOW(GLOBALS->mainwindow));
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 0));
    }
    return (TCL_OK);
}

static int gtkwavetcl_showSignal(ClientData clientData,
                                 Tcl_Interp *interp,
                                 int objc,
                                 Tcl_Obj *CONST objv[])
{
    if (objc == 3) {
        char *s0 = get_Tcl_string(objv[1]);
        char *s1;
        int row;
        unsigned location;

        sscanf(s0, "%d", &row);
        if (row < 0) {
            row = 0;
        };

        s1 = get_Tcl_string(objv[2]);
        sscanf(s1, "%u", &location);

        SetTraceScrollbarRowValue(row, location);
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 2));
    }

    return (TCL_OK);
}

/*
 * swap to a given context based on tab number (from Tcl)
 */
static gint switch_to_tab_number(unsigned int i)
{
    if (i < GLOBALS->num_notebook_pages) {
        struct Global *g_old = GLOBALS;
        /* printf("Switching to: %d\n", i); */

        set_GLOBALS((*GLOBALS->contexts)[i]);

        GLOBALS->lxt_clock_compress_to_z = g_old->lxt_clock_compress_to_z;
        GLOBALS->autoname_bundles = g_old->autoname_bundles;
        GLOBALS->autocoalesce_reversal = g_old->autocoalesce_reversal;
        GLOBALS->autocoalesce = g_old->autocoalesce;
        GLOBALS->wave_scrolling = g_old->wave_scrolling;
        GLOBALS->constant_marker_update = g_old->constant_marker_update;
        GLOBALS->do_zoom_center = g_old->do_zoom_center;
        GLOBALS->use_roundcaps = g_old->use_roundcaps;
        GLOBALS->do_resize_signals = g_old->do_resize_signals;
        GLOBALS->initial_signal_window_width = g_old->initial_signal_window_width;
        GLOBALS->use_full_precision = g_old->use_full_precision;
        GLOBALS->show_base = g_old->show_base;
        GLOBALS->display_grid = g_old->display_grid;
        GLOBALS->highlight_wavewindow = g_old->highlight_wavewindow;
        GLOBALS->disable_mouseover = g_old->disable_mouseover;
        GLOBALS->zoom_pow10_snap = g_old->zoom_pow10_snap;

        GLOBALS->scale_to_time_dimension = g_old->scale_to_time_dimension;
        GLOBALS->zoom_dyn = g_old->zoom_dyn;
        GLOBALS->zoom_dyne = g_old->zoom_dyne;

        gtk_notebook_set_current_page(GTK_NOTEBOOK(GLOBALS->notebook), GLOBALS->this_context_page);
        return (TRUE);
    }

    return (FALSE);
}

static int gtkwavetcl_setTabActive(ClientData clientData,
                                   Tcl_Interp *interp,
                                   int objc,
                                   Tcl_Obj *CONST objv[])
{
    if (objc == 2) {
        gint rc;

        if (!GLOBALS->in_tcl_callback) {
            char *s = get_Tcl_string(objv[1]);
            unsigned int tabnum = atoi(s);
            rc = switch_to_tab_number(tabnum);

            MaxSignalLength();
            gw_signal_list_force_redraw(GW_SIGNAL_LIST(GLOBALS->signalarea));

            gtkwave_main_iteration();
        } else {
            gtkwavetcl_setvar_nonblocking(WAVE_TCLCB_ERROR,
                                          "gtkwave::setTabActive prohibited in callback",
                                          WAVE_TCLCB_ERROR_FLAGS);
            rc = -1;
        }

        return (gtkwavetcl_printInteger(clientData, interp, objc, objv, rc));
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }
}

static int gtkwavetcl_getNumTabs(ClientData clientData,
                                 Tcl_Interp *interp,
                                 int objc,
                                 Tcl_Obj *CONST objv[])
{
    int value = GLOBALS->num_notebook_pages;
    return (gtkwavetcl_printInteger(clientData, interp, objc, objv, value));
}

static int gtkwavetcl_installFileFilter(ClientData clientData,
                                        Tcl_Interp *interp,
                                        int objc,
                                        Tcl_Obj *CONST objv[])
{
    if (objc == 2) {
        char *s = get_Tcl_string(objv[1]);
        unsigned int which = atoi(s);
        gint rc = install_file_filter(which);

        gtkwave_main_iteration();
        return (gtkwavetcl_printInteger(clientData, interp, objc, objv, rc));
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }
}

static int gtkwavetcl_setCurrentTranslateFile(ClientData clientData,
                                              Tcl_Interp *interp,
                                              int objc,
                                              Tcl_Obj *CONST objv[])
{
    if (objc == 2) {
        char *s = get_Tcl_string(objv[1]);
        set_current_translate_file(s);

        return (gtkwavetcl_printInteger(clientData,
                                        interp,
                                        objc,
                                        objv,
                                        GLOBALS->current_translate_file));
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }
}

static int gtkwavetcl_setCurrentTranslateEnums(ClientData clientData,
                                               Tcl_Interp *interp,
                                               int objc,
                                               Tcl_Obj *CONST objv[])
{
    if (objc == 2) {
        char *s = get_Tcl_string(objv[1]);
        set_current_translate_enums(s);

        return (gtkwavetcl_printInteger(clientData,
                                        interp,
                                        objc,
                                        objv,
                                        GLOBALS->current_translate_file));
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }
}

static int gtkwavetcl_installProcFilter(ClientData clientData,
                                        Tcl_Interp *interp,
                                        int objc,
                                        Tcl_Obj *CONST objv[])
{
    if (objc == 2) {
        char *s = get_Tcl_string(objv[1]);
        unsigned int which = atoi(s);
        gint rc = install_proc_filter(which);

        gtkwave_main_iteration();
        return (gtkwavetcl_printInteger(clientData, interp, objc, objv, rc));
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }
}

static int gtkwavetcl_setCurrentTranslateProc(ClientData clientData,
                                              Tcl_Interp *interp,
                                              int objc,
                                              Tcl_Obj *CONST objv[])
{
    if (objc == 2) {
        char *s = get_Tcl_string(objv[1]);
        set_current_translate_proc(s);

        return (gtkwavetcl_printInteger(clientData,
                                        interp,
                                        objc,
                                        objv,
                                        GLOBALS->current_translate_proc));
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }
}

static int gtkwavetcl_installTransFilter(ClientData clientData,
                                         Tcl_Interp *interp,
                                         int objc,
                                         Tcl_Obj *CONST objv[])
{
    if (objc == 2) {
        char *s = get_Tcl_string(objv[1]);
        unsigned int which = atoi(s);
        gint rc = install_ttrans_filter(which);

        gtkwave_main_iteration();
        return (gtkwavetcl_printInteger(clientData, interp, objc, objv, rc));
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }
}

static int gtkwavetcl_setCurrentTranslateTransProc(ClientData clientData,
                                                   Tcl_Interp *interp,
                                                   int objc,
                                                   Tcl_Obj *CONST objv[])
{
    if (objc == 2) {
        char *s = get_Tcl_string(objv[1]);
        set_current_translate_ttrans(s);

        return (gtkwavetcl_printInteger(clientData,
                                        interp,
                                        objc,
                                        objv,
                                        GLOBALS->current_translate_ttrans));
    } else {
        return (gtkwavetcl_badNumArgs(clientData, interp, objc, objv, 1));
    }
}

tcl_cmdstruct gtkwave_commands[] = {
    {"addCommentTracesFromList", gtkwavetcl_addCommentTracesFromList},
    {"addSignalsFromList", gtkwavetcl_addSignalsFromList},
    {"deleteSignalsFromList", gtkwavetcl_deleteSignalsFromList},
    {"deleteSignalsFromListIncludingDuplicates",
     gtkwavetcl_deleteSignalsFromListIncludingDuplicates},
    {"findNextEdge", gtkwavetcl_findNextEdge},
    {"findPrevEdge", gtkwavetcl_findPrevEdge},
    {"forceOpenTreeNode", gtkwavetcl_forceOpenTreeNode},
    {"getArgv", gtkwavetcl_getArgv},
    {"getBaselineMarker", gtkwavetcl_getBaselineMarker},
    {"getDisplayedSignals", gtkwavetcl_getDisplayedSignals},
    {"getDumpFileName", gtkwavetcl_getDumpFileName},
    {"getDumpType", gtkwavetcl_getDumpType},
    {"getFacDir", gtkwavetcl_getFacDir},
    {"getFacDtype", gtkwavetcl_getFacDtype},
    {"getFacName", gtkwavetcl_getFacName},
    {"getFacVtype", gtkwavetcl_getFacVtype},
    {"getFontHeight", gtkwavetcl_getFontHeight},
    {"getFromEntry", gtkwavetcl_getFromEntry},
    {"getHierMaxLevel", gtkwavetcl_getHierMaxLevel},
    {"getLeftJustifySigs", gtkwavetcl_getLeftJustifySigs},
    {"getLongestName", gtkwavetcl_getLongestName},
    {"getMarker", gtkwavetcl_getMarker},
    {"getMaxTime", gtkwavetcl_getMaxTime},
    {"getMinTime", gtkwavetcl_getMinTime},
    {"getNamedMarker", gtkwavetcl_getNamedMarker},
    {"getNumFacs", gtkwavetcl_getNumFacs},
    {"getNumTabs", gtkwavetcl_getNumTabs},
    {"getPixelsUnitTime", gtkwavetcl_getPixelsUnitTime},
    {"getSaveFileName", gtkwavetcl_getSaveFileName},
    {"getStemsFileName", gtkwavetcl_getStemsFileName},
    {"getTimeDimension", gtkwavetcl_getTimeDimension},
    {"getTimeZero", gtkwavetcl_getTimeZero},
    {"getToEntry", gtkwavetcl_getToEntry},
    {"getTotalNumTraces", gtkwavetcl_getTotalNumTraces},
    {"getTraceFlagsFromIndex", gtkwavetcl_getTraceFlagsFromIndex},
    {"getTraceFlagsFromName", gtkwavetcl_getTraceFlagsFromName},
    {"getTraceNameFromIndex", gtkwavetcl_getTraceNameFromIndex},
    {"getTraceScrollbarRowValue", gtkwavetcl_getTraceScrollbarRowValue},
    {"getTraceValueAtMarkerFromIndex", gtkwavetcl_getTraceValueAtMarkerFromIndex},
    {"getTraceValueAtMarkerFromName", gtkwavetcl_getTraceValueAtMarkerFromName},
    {"getTraceValueAtNamedMarkerFromName", gtkwavetcl_getTraceValueAtNamedMarkerFromName},
    {"getUnitTimePixels", gtkwavetcl_getUnitTimePixels},
    {"getVisibleNumTraces", gtkwavetcl_getVisibleNumTraces},
    {"getWaveHeight", gtkwavetcl_getWaveHeight},
    {"getWaveWidth", gtkwavetcl_getWaveWidth},
    {"getWindowEndTime", gtkwavetcl_getWindowEndTime},
    {"getWindowStartTime", gtkwavetcl_getWindowStartTime},
    {"getZoomFactor", gtkwavetcl_getZoomFactor},
    {"highlightSignalsFromList", gtkwavetcl_highlightSignalsFromList},
    {"installFileFilter", gtkwavetcl_installFileFilter},
    {"installProcFilter", gtkwavetcl_installProcFilter},
    {"installTransFilter", gtkwavetcl_installTransFilter},
    {"loadFile", gtkwavetcl_loadFile},
    {"nop", gtkwavetcl_nop},
    {"presentWindow", gtkwavetcl_presentWindow},
    {"processTclList", gtkwavetcl_processTclList}, /* not for general-purpose use */
    {"reLoadFile", gtkwavetcl_reLoadFile},
    {"setBaselineMarker", gtkwavetcl_setBaselineMarker},
    {"setCurrentTranslateEnums", gtkwavetcl_setCurrentTranslateEnums},
    {"setCurrentTranslateFile", gtkwavetcl_setCurrentTranslateFile},
    {"setCurrentTranslateProc", gtkwavetcl_setCurrentTranslateProc},
    {"setCurrentTranslateTransProc", gtkwavetcl_setCurrentTranslateTransProc},
    {"setFromEntry", gtkwavetcl_setFromEntry},
    {"setLeftJustifySigs", gtkwavetcl_setLeftJustifySigs},
    {"setMarker", gtkwavetcl_setMarker},
    {"setNamedMarker", gtkwavetcl_setNamedMarker},
    {"setTabActive", gtkwavetcl_setTabActive},
    {"setToEntry", gtkwavetcl_setToEntry},
    {"setTraceHighlightFromIndex", gtkwavetcl_setTraceHighlightFromIndex},
    {"setTraceHighlightFromNameMatch", gtkwavetcl_setTraceHighlightFromNameMatch},
    {"setTraceScrollbarRowValue", gtkwavetcl_setTraceScrollbarRowValue},
    {"setWindowStartTime", gtkwavetcl_setWindowStartTime},
    {"setZoomFactor", gtkwavetcl_setZoomFactor},
    {"setZoomRangeTimes", gtkwavetcl_setZoomRangeTimes},
    {"showSignal", gtkwavetcl_showSignal},
    {"signalChangeList",
     gtkwavetcl_signalChangeList}, /* changed from signal_change_list for consistency! */
    {"unhighlightSignalsFromList", gtkwavetcl_unhighlightSignalsFromList},
    {"", NULL} /* sentinel */
};

#else

static void dummy_function(void)
{
    /* nothing */
}

#endif
